package com.springboot.nacos.config.parse;

import small.common.core.constants.CommonConstants;
import small.common.core.constants.ConfigType;
import small.common.core.util.StringUtil;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

public class NacosDataParserHandler {

    private static List<PropertySourceLoader> propertySourceLoaders;

    private NacosDataParserHandler() {
        propertySourceLoaders = SpringFactoriesLoader
                .loadFactories(PropertySourceLoader.class, getClass().getClassLoader());
    }

    public static NacosDataParserHandler getInstance() {
        return ParserHandler.HANDLER;
    }

    /**
     * 解析数据源
     *
     * @param configName
     * @param configValue
     * @param extension
     * @return
     * @throws IOException
     */
    public List<PropertySource<?>> parseNacosData(String configName, String configValue, String extension) throws IOException {

        if (StringUtil.isBlank(configValue)) {
            return Collections.emptyList();
        }
        if (StringUtil.isBlank(extension)) {
            extension = this.getFileExtension(configName);
        }

        for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) {
            if (!canLoadFileExtension(propertySourceLoader, extension)) {
                continue;
            }

            ByteArrayResource nacosByteArrayResource;
            if (propertySourceLoader instanceof PropertiesPropertySourceLoader) {
                //PropertiesPropertySourceLoader内部是使用ISO_8859_1，中文会乱码，需要转换成unicode。
                nacosByteArrayResource = new ByteArrayResource(selectiveConvertUnicode(configValue).getBytes(), configName);
            } else {
                nacosByteArrayResource = new ByteArrayResource(configValue.getBytes(), configName);
            }

            List<PropertySource<?>> propertySourceList = propertySourceLoader
                    .load(configName, nacosByteArrayResource);
            if (CollectionUtils.isEmpty(propertySourceList)) {
                return Collections.emptyList();
            }
            //过滤空数据
            return propertySourceList.stream().filter(Objects::nonNull)
                    .map(propertySource -> {
                        if (propertySource instanceof EnumerablePropertySource) {
                            String[] propertyNames = ((EnumerablePropertySource) propertySource)
                                    .getPropertyNames();
                            if (propertyNames != null && propertyNames.length > 0) {
                                Map<String, Object> map = new LinkedHashMap<>();
                                Arrays.stream(propertyNames).forEach(name -> {
                                    map.put(name, propertySource.getProperty(name));
                                });
                                return new OriginTrackedMapPropertySource(
                                        propertySource.getName(), map, true);
                            }
                        }
                        return propertySource;
                    }).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    private boolean canLoadFileExtension(PropertySourceLoader loader, String extension) {
        return Arrays.stream(loader.getFileExtensions())
                .anyMatch((fileExtension) -> StringUtil.endsWithIgnoreCase(extension,
                        fileExtension));
    }

    private String getFileExtension(String name) {
        if (StringUtil.isBlank(name)) {
            return ConfigType.PROPERTIES.getType();
        }
        int idx = name.lastIndexOf(CommonConstants.DOT);
        if (idx > 0 && idx < name.length() - 1) {
            return name.substring(idx + 1);
        }
        return ConfigType.PROPERTIES.getType();
    }

    public static String selectiveConvertUnicode(String configValue) {
        StringBuilder sb = new StringBuilder();
        char[] chars = configValue.toCharArray();
        for (char aChar : chars) {
            if (isBaseLetter(aChar)) {
                sb.append(aChar);
            } else {
                sb.append(String.format("\\u%04x", (int) aChar));
            }
        }
        return sb.toString();
    }

    public static boolean isBaseLetter(char ch) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch);
        return ub == Character.UnicodeBlock.BASIC_LATIN || Character.isWhitespace(ch);
    }

    private static class ParserHandler {
        private static final NacosDataParserHandler HANDLER = new NacosDataParserHandler();
    }


}
